package com.atguigu.gmall.item.service.impl;

import com.atguigu.gmall.common.constans.RedisConstant;
import com.atguigu.gmall.feign.list.ListFeignClient;
import com.atguigu.gmall.feign.product.ProductFeign;
import com.atguigu.gmall.serviceutil.annotation.AppCache;
import com.atguigu.gmall.item.service.SkuDetailItemService;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.model.product.SpuSaleAttr;
import com.atguigu.gmall.model.to.SkuDetailTo;
import com.atguigu.gmall.serviceutil.service.CacheService;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author A
 * @version 1.0
 * @description: TODO
 * @date 2025/8/29 20:39
 */
@Slf4j
@Service
public class SkuDetailItemServiceImpl implements SkuDetailItemService {
    @Autowired
    private ProductFeign productFeign;
    @Autowired
    private ListFeignClient listFeignClient;
    @Autowired
    private ThreadPoolExecutor coreThreadPool;
    @Autowired
    private CacheService cacheService;
    @Qualifier("skuIdBloom")
    @Autowired
    private RBloomFilter<Object> skuIdBloom;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    @AppCache(cacheKey = RedisConstant.SKU_DETAIL_PREFIX+"#{#args[0]}",
            bloomName = RedisConstant.SKU_ID_BLOOM_NAME,
            bloomValue = "#{#args[0]}")
    @Override
    //使用aop切面进行方法拦截
    public SkuDetailTo getSkuDetail(Long skuId) {

        return getSkuDetailFromDB(skuId);
    }
    //异步更新商品的评分
    @Override
    public void updateScoreASync(Long skuId) {
        Double score = redisTemplate.opsForZSet().incrementScore(RedisConstant.SKU_SCORE_PREFIX, skuId.toString(), 1.0);
        if (score %100==0){
            //更新商品的评分
            listFeignClient.updateScore(skuId,score.longValue());
        }
    }


    //使用redisson的分布式锁
    public SkuDetailTo getSkuDetailByRedisson(Long skuId) {
        String skuCacheKey = RedisConstant.SKU_DETAIL_PREFIX + skuId; //sku详情缓存key
        String lockKey = RedisConstant.LOCK_SKU_DETAIL_PREFIX + skuId; //sku详情锁key
        //查缓存
        SkuDetailTo skuDetailTo = cacheService.getCacheData(skuCacheKey, new TypeReference<SkuDetailTo>() {
        });
        if (skuDetailTo ==null){
            log.info("该skuId:{}未命中缓存,开始查询布隆过滤器",skuId);
            //判断是否在布隆过滤器中
            boolean isContains = skuIdBloom.contains(skuId);
            if (!isContains){
                log.info("该skuId:{}不在布隆过滤器中,直接返回null",skuId);
                return null;
            }
            log.info("该skuId:{}在布隆过滤器中,开始查询数据库",skuId);
            //查询数据库前先加分布式锁,避免缓存击穿
            boolean isLock=false;
            RLock lock = redissonClient.getLock(lockKey);
            isLock = lock.tryLock();
            if (isLock){
                log.info("加锁成功,skuDetail:{}查询数据库",skuId);
                try {
                    SkuDetailTo skuDetailFromDB = getSkuDetailFromDB(skuId);
                    log.info("查询数据库成功,将skuDetail:{}缓存到redis中",skuId);
                    cacheService.setCacheData(skuCacheKey, skuDetailFromDB);
                    log.info("skuDetail:{}缓存成功",skuId);
                    return skuDetailFromDB;
                }finally {
                    try {
                        lock.unlock();
                        log.info("释放锁:{}",skuId);
                    } catch (Exception e) {
                        log.error("释放锁:{}异常",skuId,e);
                    }
                }


            }else {
                log.info("加锁失败,skuDetail:{}等待1s后查询缓存",skuId);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    log.error("skuDetail:{}查询缓存时睡眠异常",skuId,e);
                }
                return cacheService.getCacheData(skuCacheKey, new TypeReference<SkuDetailTo>() {
                });
            }

        }
        log.info("命中缓存,直接返回skuDetail:{}",skuId);
        return skuDetailTo;
    }
    //redis原生的分布式锁
    public SkuDetailTo getSkuDetailByRedis(Long skuId) {
        String skuCacheKey = RedisConstant.SKU_DETAIL_PREFIX + skuId;
        String lockKey = RedisConstant.LOCK_SKU_DETAIL_PREFIX + skuId;

        //从缓存中获取sku详情
        SkuDetailTo skuDetailToData = cacheService.getCacheData(skuCacheKey, new TypeReference<SkuDetailTo>() {
                }
        );

        if (skuDetailToData == null) {
            log.info("该skuId:{}未命中缓存的sku详情,查询bloomFilter", skuId);
            //未命中缓存,先从bloomFilter中判断是否有该skuId
            boolean isContains = skuIdBloom.contains(skuId);
            if (!isContains) {
                log.info("该skuId:{}不在bloomFilter中,布隆过滤器拦截打回", skuId);
                return null;
            }
            log.info("该skuId:{}在bloomFilter中,查询数据库", skuId);
            //从数据库中获取sku详情
            //缓存击穿,给此查询加上分布式锁,避免缓存击穿
            //Boolean为true表示加锁成功,false表示加锁失败,原子加锁
            String token = UUID.randomUUID().toString();
            Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, token, 10, TimeUnit.SECONDS);
            if (Boolean.TRUE.equals(lock)) {
                log.info("{}加锁成功,真的查询数据库", skuId);
                try {
                    skuDetailToData = getSkuDetailFromDB(skuId);
                    cacheService.setCacheData(RedisConstant.SKU_DETAIL_PREFIX + skuId, skuDetailToData);
                    log.info("{}查询数据库成功,将sku详情缓存到redis中", skuId);
                } finally {
                    log.info("释放锁:{}", token);
                    //lua脚本删除锁,原子操作
                    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                    Long executed = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(lockKey), token);
                    if (executed == 1L) {
                        log.info("{}释放锁成功", skuId);
                    } else {
                        log.info("{}释放锁失败,这是别人的锁,忽略", skuId);
                    }
                }
            } else {
                log.info("{}加锁失败,等待1s后查询缓存", skuId);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                skuDetailToData = cacheService.getCacheData(RedisConstant.SKU_DETAIL_PREFIX + skuId, new TypeReference<SkuDetailTo>() {
                });
                log.info("等待1s后查询缓存成功,直接返回skuId:{}详情", skuId);


                return skuDetailToData;
            }

        }
        log.info("命中缓存,直接返回skuId:{}详情", skuId);
        return skuDetailToData;
    }

    //从数据库中获取sku详情
    public SkuDetailTo getSkuDetailFromDB(Long skuId) {
        SkuDetailTo skuDetailTo = new SkuDetailTo();
        //sku的基本信息
        //SkuInfo skuInfo;
        CompletableFuture<Void> futureSkuInfoAndCategory = CompletableFuture.supplyAsync(() -> {
            SkuInfo skuInfo = productFeign.getSkuInfo(skuId);
            skuDetailTo.setSkuInfo(skuInfo);
            return skuInfo;
        }, coreThreadPool).thenAcceptAsync(skuInfo -> {
            //sku所属的三级分类详情
            //BaseCategoryView categoryView;
            BaseCategoryView categoryView = productFeign.getCategoryView(skuInfo.getCategory3Id());
            skuDetailTo.setCategoryView(categoryView);
        }, coreThreadPool);

        CompletableFuture<Void> futurePrice = CompletableFuture.runAsync(() -> {
            //最新价格
            //BigDecimal price;
            BigDecimal skuInfoPrice = productFeign.getSkuInfoPrice(skuId);
            skuDetailTo.setPrice(skuInfoPrice);
        }, coreThreadPool);
        CompletableFuture<Void> futureSpuSaleAttrList = CompletableFuture.runAsync(() -> {
            //spu所有销售属性值集合
            //List<SpuSaleAttr> spuSaleAttrList;
            List<SpuSaleAttr> spuSaleAttr = productFeign.getSpuSaleAttr(skuId);
            skuDetailTo.setSpuSaleAttrList(spuSaleAttr);
        }, coreThreadPool);
        CompletableFuture<Void> futureValuesSkuJson = CompletableFuture.runAsync(() -> {
            //所有可用的销售属性集合
            //String valuesSkuJson;
            String valueJson = productFeign.getValueJson(skuId);
            skuDetailTo.setValuesSkuJson(valueJson);
        }, coreThreadPool);
        CompletableFuture.allOf(futureSkuInfoAndCategory, futurePrice, futureSpuSaleAttrList, futureValuesSkuJson).join();

        return skuDetailTo;
    }



}
